Add detection for libjasper, used by the gdk-pixbuf JPEG2000 loader
authorBastien Nocera <hadess@hadess.net>
Sun, 25 Nov 2007 18:07:01 +0000 (18:07 +0000)
committerBastien Nocera <hadess@src.gnome.org>
Sun, 25 Nov 2007 18:07:01 +0000 (18:07 +0000)
2007-11-25  Bastien Nocera  <hadess@hadess.net>

* configure.in: Add detection for libjasper, used by the
gdk-pixbuf JPEG2000 loader

2007-11-25  Bastien Nocera  <hadess@hadess.net>

* Makefile.am:
* io-jasper.c: Add the libjasper JPEG2000 loader
(Closes: #469901)

svn path=/trunk/; revision=19042

ChangeLog
configure.in
gdk-pixbuf/ChangeLog
gdk-pixbuf/Makefile.am
gdk-pixbuf/io-jasper.c [new file with mode: 0644]

index e80f2d747bcf24db86ba28a37290b1c124c0a023..e933ff91bcc5a0c9f4aee594445975590045961c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2007-11-25  Bastien Nocera  <hadess@hadess.net>
+
+       * configure.in: Add detection for libjasper, used by the
+       gdk-pixbuf JPEG2000 loader
+
 2007-11-25  Matthias Clasen  <mclasen@redhat.com>
 
        * configure.in: Require gtk-doc 1.8
index 9c2cc9b83399b90b53decb35bb2e8155523b13ca..e17ba8478ec1e67fd23d7ff8862b3b67b1dc021c 100644 (file)
@@ -757,6 +757,9 @@ AC_ARG_WITH(libjpeg,
 AC_ARG_WITH(libtiff,
             [AC_HELP_STRING([--without-libtiff],
                             [disable TIFF loader for gdk-pixbuf])])
+AC_ARG_WITH(libjasper,
+            [AC_HELP_STRING([--without-libjasper],
+                            [disable JPEG2000 loader for gdk-pixbuf])])
 
 dnl Test for libtiff
   if test x$with_libtiff != xno && test -z "$LIBTIFF"; then
@@ -858,9 +861,21 @@ dnl Test for libpng
 *** from CVS.])
   fi
 
+dnl Test for libjasper
+  if test x$with_libjasper != xno && test -z "$LIBJASPER"; then
+    AC_CHECK_LIB(jasper, jas_init, LIBJASPER=-ljasper, [])
+  fi
+
+  if test x$with_libjasper != xno && test -z "$LIBJASPER"; then
+     AC_MSG_ERROR([
+*** Checks for JPEG2000 loader failed. You can build without it by passing 
+*** --without-libjasper to configure])
+  fi
+
 AC_SUBST(LIBTIFF)
 AC_SUBST(LIBJPEG)
 AC_SUBST(LIBPNG)
+AC_SUBST(LIBJASPER)
 
 AM_CONDITIONAL(BUILD_DYNAMIC_MODULES, $dynworks)
 
@@ -886,7 +901,7 @@ else
    fi
 fi
 
-all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns"
+all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns,jasper"
 included_loaders=""
 # If no loaders specified, include all
 if test "x$with_included_loaders" = xyes ; then
@@ -931,6 +946,7 @@ AM_CONDITIONAL(INCLUDE_XBM, [test x"$INCLUDE_xbm" = xyes])
 AM_CONDITIONAL(INCLUDE_TGA, [test x"$INCLUDE_tga" = xyes])
 AM_CONDITIONAL(INCLUDE_PCX, [test x"$INCLUDE_pcx" = xyes])
 AM_CONDITIONAL(INCLUDE_ICNS, [test x"$INCLUDE_icns" = xyes])
+AM_CONDITIONAL(INCLUDE_JASPER, [test x"$INCLUDE_jasper" = xyes])
 
 AC_HEADER_SYS_WAIT
 
@@ -939,6 +955,7 @@ AC_TYPE_SIGNAL
 AM_CONDITIONAL(HAVE_TIFF, test "x$LIBTIFF" != x)
 AM_CONDITIONAL(HAVE_PNG, test "x$LIBPNG" != x)
 AM_CONDITIONAL(HAVE_JPEG, test "x$LIBJPEG" != x)
+AM_CONDITIONAL(HAVE_JASPER, test "x$LIBJASPER" != x)
 
 if $dynworks ; then
   STATIC_LIB_DEPS=
index 02e60f8153843a772b27ae5c3019ab6092157121..f3be42fb6cd58793e1aea2b954bb29eaae9a5853 100644 (file)
@@ -1,3 +1,9 @@
+2007-11-25  Bastien Nocera  <hadess@hadess.net>
+
+       * Makefile.am:
+       * io-jasper.c: Add the libjasper JPEG2000 loader
+       (Closes: #469901)
+
 2007-11-20  Matthias Clasen  <mclasen@redhat.com>
 
        * gdk-pixbuf-animation.c: Use G_DEFINE_TYPE (#469341, 
index 4e7d780de4095f5f9fb840a4c424f6fced6423f4..272cc31501cd5ec484048e34e2d93b66a1cb24bf 100644 (file)
@@ -181,6 +181,14 @@ libpixbufloader_pcx_la_SOURCES = io-pcx.c
 libpixbufloader_pcx_la_LDFLAGS = -avoid-version -module $(no_undefined)
 libpixbufloader_pcx_la_LIBADD = $(module_libs)
 
+#
+# The JPEG2000 loader
+#
+libstatic_pixbufloader_jasper_la_SOURCES = io-jasper.c
+libpixbufloader_jasper_la_SOURCES = io-jasper.c
+libpixbufloader_jasper_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libpixbufloader_jasper_la_LIBADD = $(LIBJASPER) $(module_libs)
+
 if HAVE_PNG
 if INCLUDE_PNG
 STATIC_PNG_LIB = libstatic-pixbufloader-png.la
@@ -277,6 +285,14 @@ else
 ICNS_LIB = libpixbufloader-icns.la
 endif
 
+if HAVE_JASPER
+if INCLUDE_JASPER
+STATIC_JASPER_LIB = libstatic-pixbufloader-jasper.la
+else
+JASPER_LIB = libpixbufloader-jasper.la
+endif
+endif
+
 if BUILD_DYNAMIC_MODULES
 
 loader_LTLIBRARIES =   \
@@ -294,7 +310,8 @@ loader_LTLIBRARIES =        \
        $(XBM_LIB)      \
        $(TGA_LIB)      \
        $(ICNS_LIB)     \
-       $(PCX_LIB)
+       $(PCX_LIB)      \
+       $(JASPER_LIB)
 
 
 endif
@@ -314,7 +331,8 @@ noinst_LTLIBRARIES =                \
        $(STATIC_XBM_LIB)       \
        $(STATIC_TGA_LIB)       \
        $(STATIC_ICNS_LIB)      \
-       $(STATIC_PCX_LIB)
+       $(STATIC_PCX_LIB)       \
+       $(STATIC_JASPER_LIB)
 
 builtin_objs = @INCLUDED_LOADER_OBJ@
 
diff --git a/gdk-pixbuf/io-jasper.c b/gdk-pixbuf/io-jasper.c
new file mode 100644 (file)
index 0000000..5194ebd
--- /dev/null
@@ -0,0 +1,305 @@
+/* JPEG 2000 loader
+ *
+ * Copyright (c) 2007 Bastien Nocera <hadess@hadess.net>
+ * Inspired by work by Ben Karel <web+moz@eschew.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gdk-pixbuf-private.h"
+#include "gdk-pixbuf-io.h"
+
+#include <jasper/jasper.h>
+
+G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module);
+G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info);
+
+struct jasper_context {
+       GdkPixbuf *pixbuf;
+
+       GdkPixbufModuleSizeFunc size_func;
+       GdkPixbufModuleUpdatedFunc updated_func;
+       GdkPixbufModulePreparedFunc prepared_func;
+       gpointer user_data;
+
+       jas_stream_t *stream;
+
+       int width, height;
+};
+
+static void
+free_jasper_context (struct jasper_context *context)
+{
+       if (!context)
+               return;
+
+       if (context->stream) {
+               jas_stream_close (context->stream);
+               context->stream = NULL;
+       }
+
+       g_free (context);
+}
+
+static gpointer
+jasper_image_begin_load (GdkPixbufModuleSizeFunc size_func,
+                        GdkPixbufModulePreparedFunc prepared_func,
+                        GdkPixbufModuleUpdatedFunc updated_func,
+                        gpointer user_data, GError **error)
+{
+       struct jasper_context *context;
+       jas_stream_t *stream;
+
+       jas_init ();
+
+       stream = jas_stream_memopen (NULL, -1);
+       if (!stream) {
+               g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for stream"));
+               return NULL;
+       }
+
+       context = g_new0 (struct jasper_context, 1);
+       if (!context)
+               return NULL;
+
+       context->size_func = size_func;
+       context->updated_func = updated_func;
+       context->prepared_func = prepared_func;
+       context->user_data = user_data;
+       context->width = context->height = -1;
+
+       context->stream = stream;
+
+       return context;
+}
+
+static gboolean
+jasper_image_try_load (struct jasper_context *context, GError **error)
+{
+       jas_image_t *raw_image, *image;
+       int num_components, colourspace_family;
+       int i, rowstride, shift;
+       guchar *pixels;
+
+       raw_image = jas_image_decode (context->stream, -1, 0);
+       if (!raw_image) {
+               g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't decode image"));
+               return FALSE;
+       }
+
+       if (context->width == -1 && context->height == -1) {
+               int width, height;
+
+               context->width = width = jas_image_cmptwidth (raw_image, 0);
+               context->height = height = jas_image_cmptheight (raw_image, 0);
+
+               if (context->size_func) {
+                       (*context->size_func) (&width, &height, context->user_data);
+
+                       if (width == 0 || height == 0) {
+                               jas_image_destroy(raw_image);
+                               g_set_error (error,
+                                            GDK_PIXBUF_ERROR,
+                                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                            _("Transformed JPEG2000 has zero width or height"));
+                               return FALSE;
+                       }
+               }
+       }
+
+       /* We only know how to handle grayscale and RGB images */
+       num_components = jas_image_numcmpts (raw_image);
+       colourspace_family = jas_clrspc_fam (jas_image_clrspc (raw_image));
+
+       if ((num_components != 3 && num_components != 4 && num_components != 1) ||
+           (colourspace_family != JAS_CLRSPC_FAM_RGB  && colourspace_family != JAS_CLRSPC_FAM_GRAY)) {
+               jas_image_destroy (raw_image);
+               g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image type currently not supported"));
+               return FALSE;
+       }
+
+       /* Apply the colour profile to the image, creating a new one */
+       if (jas_image_clrspc (raw_image) != JAS_CLRSPC_SRGB) {
+               jas_cmprof_t *profile;
+
+               profile = jas_cmprof_createfromclrspc (JAS_CLRSPC_SRGB);
+               if (!profile) {
+                       jas_image_destroy (raw_image);
+                       g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for color profile"));
+                       return FALSE;
+               }
+
+               image = jas_image_chclrspc (raw_image, profile, JAS_CMXFORM_INTENT_PER);
+               if (!image) {
+                       jas_image_destroy (raw_image);
+                       g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for color profile"));
+                       return FALSE;
+               }
+       } else {
+               image = raw_image;
+       }
+
+       if (!context->pixbuf) {
+               int bits_per_sample;
+               guchar *data;
+
+               /* Unfortunately, gdk-pixbuf doesn't support 16 bpp images
+                * bits_per_sample = jas_image_cmptprec (image, 0);
+               if (bits_per_sample < 8)
+                       bits_per_sample = 8;
+               else if (bits_per_sample > 8)
+                       bits_per_sample = 16;
+               */
+               bits_per_sample = 8;
+
+               data = g_try_malloc0 (context->width * context->height * bits_per_sample / 8);
+               if (data == NULL) {
+                       g_set_error (error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                    _("Insufficient memory to open JPEG 2000 file"));
+                       return FALSE;
+               }
+               context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+                                                 FALSE, bits_per_sample,
+                                                 context->width, context->height);
+               if (context->prepared_func)
+                       context->prepared_func (context->pixbuf, NULL, context->user_data);
+       }
+
+       /* We calculate how much we should shift the pixel
+        * data by to make it fit into our pixbuf */
+       shift = MAX (jas_image_cmptprec (image, 0) - gdk_pixbuf_get_bits_per_sample (context->pixbuf), 0);
+
+       /* Loop over the 3 colourspaces */
+       rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
+       pixels = gdk_pixbuf_get_pixels (context->pixbuf);
+
+       for (i = 0; i < num_components; i++) {
+               jas_matrix_t *matrix;
+               int j;
+
+               matrix = jas_matrix_create (context->height, context->width);
+
+               /* in libjasper, R is 0, G is 1, etc. we're lucky :)
+                * but we need to handle the "opacity" channel ourselves */
+               if (i != 4) {
+                       jas_image_readcmpt (image, i, 0, 0, context->width, context->height, matrix);
+               } else {
+                       jas_image_readcmpt (image, JAS_IMAGE_CT_OPACITY, 0, 0, context->width, context->height, matrix);
+               }
+
+               for (j = 0; j < context->height; j++) {
+                       int k;
+
+                       for (k = 0; k < context->width; k++) {
+                               if (num_components == 3 || num_components == 4) {
+                                       pixels[j * rowstride + k * 3 + i] = jas_matrix_get (matrix, j, k) >> shift;
+                               } else {
+                                       pixels[j * rowstride + k * 3] =
+                                               pixels[j * rowstride + k * 3 + 1] =
+                                               pixels[j * rowstride + k * 3 + 2] = jas_matrix_get (matrix, j, k) >> shift;
+                               }
+                       }
+                       /* Update once per line for the last component, otherwise
+                        * we might contain garbage */
+                       if (context->updated_func && (i == num_components - 1) && k != 0) {
+                               context->updated_func (context->pixbuf, 0, j, k, 1, context->user_data);
+                       }
+               }
+
+               jas_matrix_destroy (matrix);
+       }
+
+       if (image != raw_image)
+               jas_image_destroy (image);
+       jas_image_destroy (raw_image);
+
+       return TRUE;
+}
+
+static gboolean
+jasper_image_stop_load (gpointer data, GError **error)
+{
+       struct jasper_context *context = (struct jasper_context *) data;
+       gboolean ret;
+
+       jas_stream_rewind (context->stream);
+       ret = jasper_image_try_load (context, error);
+
+       free_jasper_context (context);
+
+       return ret;
+}
+
+static gboolean
+jasper_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error)
+{
+       struct jasper_context *context = (struct jasper_context *) data;
+
+       if (jas_stream_write (context->stream, buf, size) < 0) {
+               g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory to buffer image data"));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+void
+fill_vtable (GdkPixbufModule * module)
+{
+       module->begin_load = jasper_image_begin_load;
+       module->stop_load = jasper_image_stop_load;
+       module->load_increment = jasper_image_load_increment;
+}
+
+void
+fill_info (GdkPixbufFormat * info)
+{
+       static GdkPixbufModulePattern signature[] = {
+               { "    jP", "!!!!  ", 100 },            /* file begins with 'jP' at offset 4 */
+               { "\xff\x4f\xff\x51\x00", NULL, 100 },  /* file starts with FF 4F FF 51 00 */
+               { NULL, NULL, 0 }
+       };
+       static gchar *mime_types[] = {
+               "image/jp2",
+               "image/jpeg2000",
+               "image/jpx",
+               NULL
+       };
+       static gchar *extensions[] = {
+               "jp2",
+               "jpc",
+               "jpx",
+               "j2k",
+               "jpf",
+               NULL
+       };
+
+       info->name = "jpeg2000";
+       info->signature = signature;
+       info->description = N_("The JPEG 2000 image format");
+       info->mime_types = mime_types;
+       info->extensions = extensions;
+       info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+       info->license = "LGPL";
+       info->disabled = FALSE;
+}
+